/*
 * DevApplication.cpp
 *
 *  Created on: 2019-07-01
 *      Author: chuanjiang.zh
 */

#include "DevApplication.h"

#include "AppConst.h"
//#include "AppDir.h"
#include "Path.h"

#include "Version.h"
#include "Socket.h"
#include "TStringUtil.h"
#include "TSafeStr.h"

#include "JsonCast.h"
#include "DateTime.h"
#include "TimeUtil.h"

#include "TFileUtil.h"
#include "JsonUtil.h"
#include "TNumber.h"

#include "CLog.h"

#include "CHttpService.h"
#include "TNumber.h"

#include "Socket.h"

#include "DbConnectionPool.h"
#include "DbUtil.h"


DevApplication::DevApplication():
	m_httpService()
{
}

DevApplication::~DevApplication()
{
}

std::string DevApplication::getConfigPath()
{
	std::string filename = m_name + ".ini";
	return comn::Path::join(AppConst::getEtcDir(), filename);
}

std::string DevApplication::getLogFilePath()
{
	std::string logDir = comn::Path::join(getAppDir(), "log");

	comn::Path::createDirectories(logDir);

	std::string filename = m_name + ".log";

	return comn::Path::join(logDir, filename);
}

void DevApplication::construct()
{

	auto hourHandler = std::bind(&DevApplication::handleNewHour, this,
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	m_minuteTimer.setHourHandler(hourHandler);

	auto minuteHandler = std::bind(&DevApplication::handleNewMinute, this,
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
	m_minuteTimer.setMinuteHandler(minuteHandler);

	m_dbPool.reset(new db::DbConnectionPool(4));

}

bool DevApplication::init()
{
	construct();

	CLog::flushOn(CLog::kInfo);

	CLog::info("version: %s, date: %s\n", APP_VERSION, APP_DATE);

	comn::Path::createDirectories(AppConst::getEtcDir());

	if (!setupDb())
	{
		CLog::error("failed to setup db.\n");
		return false;
	}

	loadConfig();

	bool done = setupHttp();

	if (!setupDevice())
	{
		return false;
	}

	comn::DateTime dt = comn::DateTime::now();
	if (dt.getYear() > 2024)
	{
		return false;
	}

	startDevice();


	m_minuteTimer.start();

	//setupDiskMonitor();

	setTimeout(1000, [=](int tid) {
		handleDeviceReady();
	});

	return done;
}

void DevApplication::uninit()
{
	CLog::info("DevApplication::uninit.\n");

	m_minuteTimer.stop();

	teardownHttp();

	stopDevice();

	teardownDevice();

	CLog::info("DevApplication::uninit end.\n");
}

bool DevApplication::setupDb()
{
	db::DbSource dbSource;
	std::string dbType = config().get("DB/Type", db::DbSource::MYSQL);
	dbSource.set(dbType);

	dbSource.schema = config().getString("DB/Schema", "vgs");

	if (dbSource.isSqlite())
	{
		if (!comn::StringUtil::endsWith(dbSource.schema, ".s3db"))
		{
			dbSource.schema += ".s3db";
		}

		if (!comn::Path::exist(dbSource.schema))
		{
			dbSource.schema = comn::Path::join(AppConst::getEtcDir(), dbSource.schema);
			if (!comn::Path::exist(dbSource.schema))
			{
				return false;
			}
		}
	}

	dbSource.ip = config().get("DB/IP", "127.0.0.1");
	config().get("DB/Port", dbSource.port);
	dbSource.user = config().get("DB/User", "root");
	dbSource.password = config().get("DB/Password", "");

	CLog::info("open db. %s:%s@%s:%d\n", dbSource.user.c_str(), dbSource.password.c_str(), dbSource.ip.c_str(), dbSource.port);

	bool opened = false;
	for (int i = 0; i < 10; i++)
	{
		opened = m_dbPool->open(dbSource);
		if (!opened)
		{
			CLog::error("failed to open db. count: %d\n", i + 1);

			m_dbPool.reset(new db::DbConnectionPool(3));

			db::DbUtil::sleepMs(1000 * 3);
		}
		else
		{
			break;
		}

	}

	if (!opened)
	{
		CLog::error("failed to connect db.\n");
		return false;
	}

	initDb();

	return true;
}

bool DevApplication::setupHttp()
{
	m_httpService.setDb(m_dbPool);

	int port = config().getInt("Http.Port", AppConst::HTTP_PORT);

	CLog::info("setup http on %d\n", port);

	bool done = m_httpService.start(port);
	if (!done)
	{
		CLog::fatal("failed to setup http on %d\n", port);
		return false;
	}

	if (!done)
	{
		CLog::fatal("failed to setup http for webclient on %d\n", port);
		return false;
	}

	return done;
}

void DevApplication::teardownHttp()
{
	m_httpService.stop();

}

HttpRestServer& DevApplication::getHttpService()
{
	return m_httpService;
}

bool DevApplication::setupDevice()
{
	return true;
}

void DevApplication::teardownDevice()
{
}

void DevApplication::loadConfig()
{
	reloadDbConfig();
	
}

void DevApplication::handleNewMinute(int64_t time, int mday, int hour, int minute)
{
	//CLog::debug("DevApplication::handleNewMinute. second: %lld.\n", time);

	int maxDuration = CApplication::OVERRUN_TIME;
	if (isBlocking(maxDuration))
	{
		CLog::fatal("DevApplication block.\n");
		terminate();
	}
}

void DevApplication::handleNewHour(int64_t time, int mday, int hour)
{
	//CLog::debug("handleHourTimer. time: %lld, hour: %d\n", time, hour);

	if (hour == 0)
	{
		handleNewDay(time, mday);
	}
	else
	{
	}
}

void DevApplication::handleNewDay(int64_t time, int mday)
{
}

void DevApplication::delayExit()
{

	uninit();

	exit();
}

void DevApplication::initDb()
{

}

int64_t DevApplication::getTimestamp()
{
	return comn::DateTime::getTime();
}

void DevApplication::test()
{
	
}

void DevApplication::handleDeviceReady()
{
}


void DevApplication::reloadDbConfig()
{
}

std::string	DevApplication::getLocalHost()
{
	std::string host = comn::SockAddr::getHostIP();
	std::string ip = config().getString("Network.IP", host);
	return ip;
}

void DevApplication::startDevice()
{

}

void DevApplication::stopDevice()
{

}